/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.channel;

import java.util.ArrayList;
import java.util.List;

import static io.netty.util.internal.ObjectUtil.checkPositive;
import static java.lang.Math.max;
import static java.lang.Math.min;

/**
 * {@link RecvByteBufAllocator}, 根据反馈自动增大和减小预测缓冲区大小.
 * <p>
 * 如果先前的 read 操作完全填充已分配的缓冲区, 它将逐步增加预期的可读 byte 数量.
 * 如果先前的 read 操作连续两次无法完全填充一定数量的已分配的缓冲区, 它将逐步减少预期的可读 byte 数量.
 * 否则, 它将继续返回相同的预测值.
 */
public class AdaptiveRecvByteBufAllocator extends DefaultMaxMessagesRecvByteBufAllocator {

    // 如果使用 idea 默认代码格式化, 将导致类初始化失败, 暂时注释, 反正也无用
    // /**
    //  * @deprecated There is state for {@link #maxMessagesPerRead()} which is typically based upon channel type.
    //  */
    // @Deprecated
    // public static final AdaptiveRecvByteBufAllocator DEFAULT = new AdaptiveRecvByteBufAllocator();

    // 默认值
    /**
     * 预期缓冲区大小的下限
     */
    static final int DEFAULT_MINIMUM = 64;
    /**
     * 没有收到反馈时的初始缓冲区大小
     */
    static final int DEFAULT_INITIAL = 1024;
    /**
     * 预期缓冲区大小的上限
     */
    static final int DEFAULT_MAXIMUM = 65536;
    /**
     * index 增加步进数量
     */
    private static final int INDEX_INCREMENT = 4;
    /**
     * index 减少步进数量
     */
    private static final int INDEX_DECREMENT = 1;
    /**
     * - 初始值: 16
     * - < 512: 步进 16
     * - >= 512: double, 直至溢出 int 范围
     */
    private static final int[] SIZE_TABLE;

    static {
        List<Integer> sizeTable = new ArrayList<Integer>();
        // 步进 16
        for (int i = 16; i < 512; i += 16) {
            sizeTable.add(i);
        }

        // double, 直至溢出 int 范围(溢出后值为负数)
        for (int i = 512; i > 0; i <<= 1) {
            sizeTable.add(i);
        }

        SIZE_TABLE = new int[sizeTable.size()];
        for (int i = 0; i < SIZE_TABLE.length; i++) {
            SIZE_TABLE[i] = sizeTable.get(i);
        }
    }

    /**
     * 预期缓冲区大小的下限的下标
     */
    private final int minIndex;
    /**
     * 预期缓冲区大小的上限的下标
     */
    private final int maxIndex;
    /**
     * 没有收到反馈时的初始缓冲区大小
     */
    private final int initial;

    /**
     * 使用默认参数创建一个新的 predictor.
     * - minimum: 64
     * - initial: 1024
     * - maximum: 65536
     */
    public AdaptiveRecvByteBufAllocator() {
        this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM);
    }

    /**
     * 使用指定的参数创建一个新的 predictor
     *
     * @param minimum 预期缓冲区大小的下限, {@link this#DEFAULT_MINIMUM}
     * @param initial 没有收到反馈时的初始缓冲区大小, {@link this#DEFAULT_INITIAL}
     * @param maximum 预期缓冲区大小的上限, {@link this#DEFAULT_MAXIMUM}
     */
    public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) {
        checkPositive(minimum, "minimum");
        if (initial < minimum) {
            throw new IllegalArgumentException("initial: " + initial);
        }
        if (maximum < initial) {
            throw new IllegalArgumentException("maximum: " + maximum);
        }

        int minIndex = getSizeTableIndex(minimum);
        if (SIZE_TABLE[minIndex] < minimum) {
            this.minIndex = minIndex + 1;
        } else {
            this.minIndex = minIndex;
        }

        int maxIndex = getSizeTableIndex(maximum);
        if (SIZE_TABLE[maxIndex] > maximum) {
            this.maxIndex = maxIndex - 1;
        } else {
            this.maxIndex = maxIndex;
        }

        this.initial = initial;
    }

    /**
     * 使用二分法查找 {@linkplain this#SIZE_TABLE SIZE_TABLE} 中大于等于该 size 的最小值的下标
     */
    private static int getSizeTableIndex(final int size) {
        for (int low = 0, high = SIZE_TABLE.length - 1; ; ) {
            if (high < low) {
                return low;
            }
            if (high == low) {
                return high;
            }

            int mid = low + high >>> 1;
            int a = SIZE_TABLE[mid];
            int b = SIZE_TABLE[mid + 1];
            if (size > b) {
                low = mid + 1;
            } else if (size < a) {
                high = mid - 1;
            } else if (size == a) {
                return mid;
            } else {
                return mid + 1;
            }
        }
    }

    @SuppressWarnings("deprecation")
    @Override
    public Handle newHandle() {
        return new HandleImpl(minIndex, maxIndex, initial);
    }

    @Override
    public AdaptiveRecvByteBufAllocator respectMaybeMoreData(boolean respectMaybeMoreData) {
        super.respectMaybeMoreData(respectMaybeMoreData);
        return this;
    }

    private final class HandleImpl extends MaxMessageHandle {
        private final int minIndex;
        private final int maxIndex;
        /**
         * 下一 receive buffer 大小 下标
         */
        private int index;
        /**
         * 下一 receive buffer 大小
         */
        private int nextReceiveBufferSize;
        /**
         * 是否减少 index
         */
        private boolean decreaseNow;

        HandleImpl(int minIndex, int maxIndex, int initial) {
            this.minIndex = minIndex;
            this.maxIndex = maxIndex;

            index = getSizeTableIndex(initial);
            nextReceiveBufferSize = SIZE_TABLE[index];
        }

        @Override
        public void lastBytesRead(int bytes) {
            // 如果我们读取的内容与要求的一样多, 则应检查是否需要增加下一个预测值的大小.
            // 当有大量数据挂起时, 这有助于更快地进行调整, 并且可以避免返回 selector 以检查是否有更多数据.
            // 返回 selector 可能会增加大量数据传输的延迟.
            if (bytes == attemptedBytesRead()) {
                record(bytes);
            }
            super.lastBytesRead(bytes);
        }

        @Override
        public int guess() {
            return nextReceiveBufferSize;
        }

        private void record(int actualReadBytes) {
            // 未填满预测大小
            if (actualReadBytes <= SIZE_TABLE[max(0, index - INDEX_DECREMENT)]) {
                // 连续两次未满足, 减少下一预测值
                if (decreaseNow) {
                    index = max(index - INDEX_DECREMENT, minIndex);
                    nextReceiveBufferSize = SIZE_TABLE[index];
                    decreaseNow = false;
                } else {
                    decreaseNow = true;
                }
            }
            // 填满预测大小, 增加下一预测值
            else if (actualReadBytes >= nextReceiveBufferSize) {
                index = min(index + INDEX_INCREMENT, maxIndex);
                nextReceiveBufferSize = SIZE_TABLE[index];
                decreaseNow = false;
            }
        }

        @Override
        public void readComplete() {
            record(totalBytesRead());
        }
    }
}
